package com.inco.cloud.auth.config;

import com.inco.cloud.common.config.IncoPasswordEncoder;
import com.inco.cloud.common.config.IncoTokenStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.http.HttpMethod;
import javax.sql.DataSource;

@Configuration
// 启用授权服务器
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    IncoPasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    DataSource dataSource;

    @Autowired
    private IncoTokenStore tokenStore;

    @Autowired
    @Qualifier("myUserDetailService")
    private UserDetailsService userDetailsService;
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // 开启/oauth/token_key验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
    }

    /**
     *配置授权服务器的客户端详情
     授权码（authorization-code）
     隐藏式（implicit）
     密码式（password）：
     客户端凭证（client credentials）
     注意，不管哪一种授权方式，第三方应用申请令牌之前，都必须先到系统备案，说明自己的身份，然后会拿到两个身份识别码：客户端 ID（client ID）和客户端密钥（client secret）。
     这是为了防止令牌被滥用，没有备案过的第三方应用，是不会拿到令牌的。
     密码方式
     http://127.0.0.1:9999/oauth/token?client_id=acme&client_secret=acmesecret&grant_type=password&username=admin&password=123456
     授权方式
     http://127.0.0.1:9999/oauth/authorize?client_id=acme&response_type=code&scope=all&redirect_uri=http://www.baidu.com
     http://127.0.0.1:9999/oauth/token?client_id=acme&client_secret=acmesecret&grant_type=authorization_code&code=ff9j04&redirect_uri=http://www.baidu.com
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
        2.配置说明
​       scopes​​:授权范围标识，比如指定微服务名称，则只可以访问指定的微服务
        autoApprove:false跳转到授权页面手动点击授权，true不需要手动授权，直接响应授权码
        redirectUris:当获取授权码后，认证服务器会重定向到指定的这个​​URL​​​，并且带着一个授权码​​code​​响应。
        withClient:允许访问此认证服务器的客户端ID
        secret:客户端密码，加密存储
        authorizedGrantTypes:授权类型，支持同时多种授权类型
        authorization_code:授权模式
        implicit:隐式授权模式(简化模式)
        password:密码模式
        client_credentials:客户端模式
        refresh_token:刷新令牌模式(只有在授权模式或者密码模式才会生效)
        accessTokenValiditySeconds :token有效时间（单位秒）
        refreshTokenValiditySeconds:刷新token有效期(单位秒)
        */
        /**
         * inMemory是存储到内存中 并未到数据库
         */
        clients.inMemory()
                //客客户端 ID（client ID）
                .withClient("acme")
                //配置申请的权限范围
                .scopes("all")
                /**
                 * 配置grant_type，表示授权类型
                 * authorization_code: 授权码
                 * password： 密码
                 * client_credentials: 客户端
                 * refresh_token: 更新令牌
                 */
                .authorizedGrantTypes("authorization_code","password","client_credentials","refresh_token")
                //客户端密钥（client secret）
                .secret(passwordEncoder.encode("acmesecret")).autoApprove(true)
                //配置访问token的有效期
                .accessTokenValiditySeconds(7200)
                //配置刷新token的有效期
                .refreshTokenValiditySeconds(7200+7200);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        endpoints.authenticationManager(authenticationManager);
        // 配置端点允许的请求方式
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        // 配置认证管理器
        endpoints.authenticationManager(authenticationManager);
        // 自定义异常翻译器，用于处理OAuth2Exception
//        endpoints.exceptionTranslator(myWebResponseExceptionTranslator);
        // 重新组装令牌颁发者，加入自定义授权模式
//        endpoints.tokenGranter(getTokenGranter(endpoints));
/*      // 添加JWT令牌
        // JWT令牌转换器
        endpoints.accessTokenConverter(jwtAccessTokenConverter);
        // Redis 存储令牌*/
        endpoints.tokenStore(tokenStore);
        endpoints.reuseRefreshTokens(false);  //refresh_token是否重复使用
        endpoints.userDetailsService(userDetailsService); //刷新令牌授权包含对用户信息的检查，设置userDetailsService刷新token时候会用到
    }
    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }
}
